#include "ShowScreenWindow.h"
#include "MainWindow.h"

ShowScreenWindow::ShowScreenWindow(int x, int y, int width, int height, QWidget* parent)
{
	/*this->setVisible(false);
	this->m_ui.setupUi(this);
	this->setParent(parent);
	this->move(x, y);
	this->resize(width, height);
	this->setVisible(true);*/
	this->setParent(parent);
	
	this->move(x, y);
	this->resize(width, height);

	this->setAttribute(Qt::WA_PaintOnScreen); // 允许DX渲染
	this->setAttribute(Qt::WA_OpaquePaintEvent);
	this->setFocusPolicy(Qt::WheelFocus); // 允许按键时间
	// this->setMouseTracking(true); // 允许鼠标移动

	this->initD3D();
	this->renderInit();

	enum_monitor(this->m_monitor_v);
	this->m_showMonitor = this->m_monitor_v->at(0);
	// this->m_showMonitor->init();
	
	// parent->setUpdatesEnabled(false);
}

void ShowScreenWindow::paintEvent(QPaintEvent* event)
{
	/*this->renderUpdate();
	this->m_swapChain->Present(0, 0);*/
}

void ShowScreenWindow::resizeEvent(QResizeEvent* event)
{
	this->resizeD3D();
}

void ShowScreenWindow::start()
{
	this->stop();
	this->m_alive = true;
	this->m_playThread = new std::thread(&ShowScreenWindow::play, this);
}

bool ShowScreenWindow::stop()
{
	this->m_alive = false;
	if (this->m_playThread != nullptr) {
		this->m_playThread->join();
		delete this->m_playThread;
		this->m_playThread = nullptr;
		return false;
	}
	return true;
}

void ShowScreenWindow::swap()
{
	if (!this->m_alive) {
		this->renderUpdate();
		this->m_swapChain->Present(0, 0);
	}
}

ShowScreenWindow::~ShowScreenWindow()
{
	this->stop();
	this->clearD3D();
	release_monitor_v(this->m_monitor_v);
}

void ShowScreenWindow::play()
{
	float bgColor[4] = { 1, 1, 0.5, 1 };
	this->setUpdatesEnabled(false);
	size_t size = this->m_showMonitor->get_monitor_buffer_size();
	uint8_t* monitor_buffer = new uint8_t[size];
	ID3D11Texture2D* target = nullptr;
	while (this->m_alive) {
		// this->m_showMonitor->get(monitor_buffer, size, target);
		QImage image(monitor_buffer, 2560, 1440, QImage::Format_RGB32);
		// this->m_d3dDeviceContext->CopySubresourceRegion(size, )
		this->updateD3D(target);
		// this->m_d3dDeviceContext->ClearRenderTargetView(this->m_renderTargetView, bgColor);
		this->m_swapChain->Present(0, 0);
		Sleep(10);
	}
	delete[] monitor_buffer;
	this->setUpdatesEnabled(true);
}

void ShowScreenWindow::initD3D()
{
	HRESULT hr;
	DXGI_MODE_DESC bufferDesc;//Dx用来描述 后台缓冲区 的结构体
	ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
	bufferDesc.Width = this->width();         //QT自带的获取窗口宽度的方法// 后台缓冲区宽度
	bufferDesc.Height = this->height();       //QT自带的获取窗口高度的方法// 后台缓冲区高度
	bufferDesc.RefreshRate.Numerator = 60;              //（注释1）  显示最大的刷新率  刷新率如果是  60pfs/每1秒
	bufferDesc.RefreshRate.Denominator = 1;             //（注释1）  显示最大刷新率
	bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;                     // 后台缓冲区像素格式
	bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; //ScanlineOrdering:用于描述扫描线绘制模式的枚举。可以设置为0
	bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;                 //用于描述缩放形式的枚举类型，默认使用DXGI_MODE_SCALING_UNSPECIFIED。
	DXGI_SWAP_CHAIN_DESC swapChainDesc;//Dx用来描述交换缓冲区的结构体

	ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
	swapChainDesc.BufferDesc = bufferDesc;                          //多重采样数量和质量级别
	swapChainDesc.SampleDesc.Count = 1;                             //（注释2）多重采样的结构体描述
	swapChainDesc.SampleDesc.Quality = 0;                           //（注释2）多重采样的结构体描述
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;    //设为 DXGI_USAGE_RENDER_TARGET_OUTPUT，因为我们要将 场景渲染到后台缓冲区
	swapChainDesc.BufferCount = 1;                                  //交换链中的后台缓冲区数量；我们一般只用一个后台缓冲区来实现双 缓存。当然，你也可以使用两个后台缓冲区来实现三缓存。
	swapChainDesc.OutputWindow = (HWND)this->winId();                     //这个非常重要，winId()是Qt中QWidget获取操作句柄的函数，这里表示我们把当前QWidget的控制权交给了DX，让DX去管理这个句柄
	swapChainDesc.Windowed = TRUE;                                  //当设为 true 时，程序以窗口模式运行；当设为 false 时，程序以全屏模式运行
	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;            //设为 DXGI_SWAP_EFFECT_DISCARD，让显卡驱动程序选择最高效 的显示模式
	//这个结构体还有一个变量Flags（一般设置为0就好） ： 可 选 的 标 志 值 。 如 果 设 为 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH，那么
	//当应用程序切换到全屏模式时，Direct3D 会自动选择与当前的后台缓冲区设置最匹配的显示模式。如果未指定该标志 值，那么当应用程序切换到全屏模式时，Direct3D 会使用当前的桌面显示模式。我们在示例 框架中没有使用该标志值，因为对于我们的演示程序来说，在全屏模式下使用当前的桌面显 示模式可以得到很好的效果
	//（注释3）这是一个最新的方法，用来同时创建设备、设备上下文和交换链，这个非常方便
	hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL,
	D3D11_SDK_VERSION, &swapChainDesc, &this->m_swapChain, &this->m_d3dDevice, NULL, &this->m_d3dDeviceContext);
	//创建缓冲区图片，可以简单理解为就是一张前台缓冲的图片，我们Dx在后台缓冲区算出来一张图片之后，需要通过Swap交换到前台来，相当于将后台的图片在窗口中显示了出来，而这个显示出来的其实本质就是一张图片
	ID3D11Texture2D* backBuffer;
	//这个方法用来获取后台缓冲区的指针，把第一个后台缓冲区和这个缓冲区连接起来
	hr = m_swapChain->GetBuffer(0,      //后台缓冲的索引值，一般是0.之所以有这个是因为后台缓冲可以有很多个
		__uuidof(ID3D11Texture2D),//这是参数接口的类型
		(void**)&backBuffer);       //目标渲染视图的图片


	//这里真正的创建了目标渲染试图，就是平时我们肉眼所看到的显示的那个界面viewport，把后台缓冲区
	hr = m_d3dDevice->CreateRenderTargetView(backBuffer, nullptr, &m_renderTargetView);
	backBuffer->Release();
	backBuffer = 0;
	// XYB_SafeDel(backBuffer)       //（注释4）这个非常重要，宏是我为了安全释放COM自定义的
		//每调用一次GetBuffer 方法，后台缓冲区的 COM 引用计数就会 向上递增一次，这便是我们在代码片段的结尾处释放它的原因
		//这个不释放你会发现绘制不了shader，但是却可以clear屏幕颜色哦


//下面就是创建深度模板缓冲区,创建过程和目标渲染视图基本一样，这里仅对不一样的参数做下注解
	D3D11_TEXTURE2D_DESC depthStencilDesc;

	depthStencilDesc.Width = (uint)this->width();
	depthStencilDesc.Height = (uint)this->height();
	depthStencilDesc.MipLevels = 1;     //mipLevels要生成几级，1表示多采样纹理，0表示会生成一整套的mipMap，这里不需要mip所以写1
	depthStencilDesc.ArraySize = 1;     //纹理数组中的纹理数量，比如立方体贴图由六个面，这边就可以设置为6，或者6的倍数
	depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;        //纹理格式
	depthStencilDesc.SampleDesc.Count = 1;
	depthStencilDesc.SampleDesc.Quality = 0;
	depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
	depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;   //绑定标识符，描述这个是一个深度模板缓冲图片，其实这个结构体就是个普通的2D纹理声明的结构体
	depthStencilDesc.CPUAccessFlags = 0;            //这个没有搞懂，后续弄懂了补充更新文章吧，有大佬知道的欢迎评论区补充下谢谢，大概是允许CPU访问的
	depthStencilDesc.MiscFlags = 0;                 //这个是控制上面那个CPU访问的

	//在GPU上根据上面那个描述创建一块用于纹理储存的内存Buffer
	hr = this->m_d3dDevice->CreateTexture2D(&depthStencilDesc, NULL, &this->m_depthStencilBuffer);
	//把这个buffer绑定给视口
	hr = this->m_d3dDevice->CreateDepthStencilView(this->m_depthStencilBuffer, NULL, &this->m_depthStencilView);


	//把目标渲染视口和深度模板关联起来
	this->m_d3dDeviceContext->OMSetRenderTargets(1, &this->m_renderTargetView, this->m_depthStencilView);

	
	//第四步，设置视口大小，D3D11默认不会设置视口，此步骤必须手动设置
	this->m_viewport.TopLeftX = 0;          //视口左上角的横坐标
	this->m_viewport.TopLeftY = 0;          //视口左上角的总坐标
	this->m_viewport.Width = this->width();         //宽
	this->m_viewport.Height = this->height();       //高
	this->m_viewport.MinDepth = 0.0f;       //深度缓存最小值0，dx里深度值是0到1范围内的，所以下限值是0
	this->m_viewport.MaxDepth = 1.0f;       //深度值最大值1，

	//刷新重置下viewport
	this->m_d3dDeviceContext->RSSetViewports(1, &this->m_viewport);
}

void ShowScreenWindow::resizeD3D()
{
	//非常重要，再刷新前需要把老的缓冲区清除掉，创建新的！因为窗口大小改变了，相当于我们用于缓存的图片大小也改变了!!
	this->m_renderTargetView->Release();
	this->m_renderTargetView = 0;
	this->m_depthStencilView->Release();
	this->m_depthStencilView = 0;
	this->m_depthStencilBuffer->Release();
	this->m_depthStencilBuffer = 0;
	HRESULT hr;
	hr = this->m_swapChain->ResizeBuffers(1, this->width(), this->height(), DXGI_FORMAT_R8G8B8A8_UNORM, 0);
	//Create our BackBuffer
	ID3D11Texture2D* backBuffer;
	hr = this->m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);

	//Create our Render Target
	hr = this->m_d3dDevice->CreateRenderTargetView(backBuffer, NULL, &this->m_renderTargetView);
	backBuffer->Release();
	backBuffer = 0;

	D3D11_TEXTURE2D_DESC depthStencilDesc;

	depthStencilDesc.Width = (uint)this->width();
	depthStencilDesc.Height = (uint)this->height();
	depthStencilDesc.MipLevels = 1;
	depthStencilDesc.ArraySize = 1;
	depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthStencilDesc.SampleDesc.Count = 1;
	depthStencilDesc.SampleDesc.Quality = 0;
	depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
	depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthStencilDesc.CPUAccessFlags = 0;
	depthStencilDesc.MiscFlags = 0;

	hr = this->m_d3dDevice->CreateTexture2D(&depthStencilDesc, NULL, &this->m_depthStencilBuffer);
	hr = this->m_d3dDevice->CreateDepthStencilView(this->m_depthStencilBuffer, NULL, &this->m_depthStencilView);

	this->m_d3dDeviceContext->OMSetRenderTargets(1, &this->m_renderTargetView, this->m_depthStencilView);

	ZeroMemory(&this->m_viewport, sizeof(D3D11_VIEWPORT));

	this->m_viewport.TopLeftX = 0;
	this->m_viewport.TopLeftY = 0;
	this->m_viewport.Width = this->width();
	this->m_viewport.Height = this->height();
	this->m_viewport.MinDepth = 0.0f;
	this->m_viewport.MaxDepth = 1.0f;

	this->m_d3dDeviceContext->RSSetViewports(1, &this->m_viewport);
}

void ShowScreenWindow::updateD3D(ID3D11Texture2D* _target)
{
	/*this->m_renderTargetView->Release();
	this->m_renderTargetView = nullptr;*/
	this->m_depthStencilView->Release();
	this->m_depthStencilView = nullptr;
	if (this->m_depthStencilBuffer != nullptr) {
		this->m_depthStencilBuffer->Release();
		this->m_depthStencilBuffer = nullptr;
	}
	HRESULT hr;

	ID3D11DeviceContext* context = nullptr;
	this->m_d3dDevice->GetImmediateContext(&context);
	D3D11_TEXTURE2D_DESC desc;
	_target->GetDesc(&desc);

	D3D11_TEXTURE2D_DESC target_desc;
	memcpy_s(&target_desc, sizeof(target_desc), &desc, sizeof(desc));
	target_desc.Width = (uint)this->width();
	target_desc.Height = (uint)this->height();
	target_desc.MipLevels = 1;     //mipLevels要生成几级，1表示多采样纹理，0表示会生成一整套的mipMap，这里不需要mip所以写1
	target_desc.ArraySize = 1;     //纹理数组中的纹理数量，比如立方体贴图由六个面，这边就可以设置为6，或者6的倍数
	target_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;        //纹理格式
	target_desc.SampleDesc.Count = 1;
	target_desc.SampleDesc.Quality = 0;
	target_desc.Usage = D3D11_USAGE_DEFAULT;
	target_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;   //绑定标识符，描述这个是一个深度模板缓冲图片，其实这个结构体就是个普通的2D纹理声明的结构体
	target_desc.CPUAccessFlags = 0;            //这个没有搞懂，后续弄懂了补充更新文章吧，有大佬知道的欢迎评论区补充下谢谢，大概是允许CPU访问的
	target_desc.MiscFlags = 0;                 //这个是控制上面那个CPU访问的

	/*target_desc.Usage = D3D11_USAGE_STAGING;
	target_desc.BindFlags = 0;
	target_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	target_desc.MiscFlags = 0;*/
	hr = this->m_d3dDevice->CreateTexture2D(&target_desc, nullptr, &this->m_depthStencilBuffer);
	if (FAILED(hr)) {
		return;
	}
	context->CopyResource(_target, this->m_depthStencilBuffer);

	hr = this->m_d3dDevice->CreateDepthStencilView(this->m_depthStencilBuffer, NULL, &this->m_depthStencilView);

	this->m_d3dDeviceContext->OMSetRenderTargets(1, &this->m_renderTargetView, this->m_depthStencilView);

	ZeroMemory(&this->m_viewport, sizeof(D3D11_VIEWPORT));

	this->m_viewport.TopLeftX = 0;
	this->m_viewport.TopLeftY = 0;
	this->m_viewport.Width = this->width();
	this->m_viewport.Height = this->height();
	this->m_viewport.MinDepth = 0.0f;
	this->m_viewport.MaxDepth = 1.0f;

	this->m_d3dDeviceContext->RSSetViewports(1, &this->m_viewport);
}

void ShowScreenWindow::renderInit()
{
}

void ShowScreenWindow::renderUpdate()
{
	this->m_d3dDeviceContext->ClearRenderTargetView(this->m_renderTargetView, this->m_bgColor);
}

void ShowScreenWindow::clearD3D()
{
	if (this->m_rasterizeState != nullptr) {
		this->m_rasterizeState->Release();
		this->m_rasterizeState = nullptr;
	}
	if (this->m_renderTargetView != nullptr) {
		this->m_renderTargetView->Release();
		this->m_renderTargetView = nullptr;
	}
	if (this->m_depthStencilView != nullptr) {
		this->m_depthStencilView->Release();
		this->m_depthStencilView = nullptr;
	}
	if (this->m_depthStencilBuffer != nullptr) {
		this->m_depthStencilBuffer->Release();
		this->m_depthStencilBuffer = nullptr;
	}
	if (this->m_swapChain != nullptr) {
		this->m_swapChain->Release();
		this->m_swapChain = nullptr;
	}
	if (this->m_d3dDeviceContext != nullptr) {
		this->m_d3dDeviceContext->Release();
		this->m_d3dDeviceContext = nullptr;
	}
	if (this->m_d3dDevice != nullptr) {
		this->m_d3dDevice->Release();
		this->m_d3dDevice = nullptr;
	}
}
